/*
 * Licensed to the Tai Ping Jin Ke
 *
 * Copyright (c) 2022 .
 * All rights reserved.
 * 项目名称：呼叫中心-请求处理层-权限控制管理
 * 版权说明：本软件属太平金融科技服务(上海)有限公司所有，在未获得太平金融科技服务(上海)有限公司正式授权情况下，任何企业和个人，不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。
 */
package com.taiping.cc.authorize.webapi.handler;

import com.taiping.cc.application.tools.SessionUserUtils;
import com.taiping.dlt.pojo.bo.AuthLoginBO;
import com.taipingframework.boot.cache.redis.StandardCacheKeyBuilder;
import com.taipingframework.boot.session.HttpSessionManager;
import com.taipingframework.boot.session.constant.SessionCacheKeyEnum;
import com.taipingframework.boot.session.core.CommonSessionHandler;
import com.taipingframework.boot.session.support.ClientDetails;
import com.taipingframework.utility.exception.ApplicationException;
import com.taipingframework.utility.extend.ServletHelper;
import com.taipingframework.utility.thread.wrapper.BenchmarksFuture;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Objects;
import java.util.Optional;

/**
 * 本地会话管理请求转发
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class SessionHandler {

    private final CommonSessionHandler sessionHandler;

    /**
     * 判断是否已经登录/判断前端拿到的token是否有效
     *
     * @return true - token有效；false - token过期
     */
    BenchmarksFuture<Boolean> loginCheck() {
        return BenchmarksFuture.supplyAsync(() -> {
            HttpServletRequest request = ServletHelper.getRequest();
            String accessToken = Optional.ofNullable(request.getHeader(StandardCacheKeyBuilder.getAuthTokenKey()))
                    .orElse(StringUtils.EMPTY);

            HttpSession httpSession = request.getSession(false);
            return Objects.nonNull(httpSession) && httpSession.getId().equalsIgnoreCase(accessToken);
        }).exceptionally(throwable -> {
            throw new ApplicationException(throwable.getCause());
        });
    }

    /**
     * 判断是否允许登录
     *
     * @return 不允许 - 返回session-id；允许登录 - 返回空字符串
     */
    BenchmarksFuture<String> loginValidate(String loginName) {
        return BenchmarksFuture.supplyAsync(() -> {
            String sessionId = sessionHandler.checkSessionConflict(loginName);
            /*
            removeSessionByForce方法不应被调用，而应该由前端通过参数`conflictSessionId`主动调用退出登录的接口，然后才能再次调用登录的接口！

            if (StringUtils.isNotEmpty(sessionId)) {
                log.info("本次登录将自动注销已存在的终端会话对象【{}】", sessionId);
                return sessionHandler.removeSessionByForce(sessionId);
            }
            return false;
            */
            if (StringUtils.isNotEmpty(sessionId)) {
                log.error("检测到当前终端会话对象【{}】已存在，需要在注销之后才能登录！", sessionId);
                return true;
            }
            return false;
        }).handleAsync((flag, throwable) -> {
            if (throwable != null) {
                throw new ApplicationException(throwable.getCause());
            }
            ClientDetails clientDetails = HttpSessionManager.readClientDetails(ServletHelper.getRequest(), loginName);
            return ClientDetails.validateForSession(clientDetails);
        }).exceptionally(throwable -> {
            throw new ApplicationException(throwable.getCause());
        });
    }

    /**
     * 创建本地会话
     */
    BenchmarksFuture<String> createLocalSession(AuthLoginBO authLoginBO) {
        return BenchmarksFuture.supplyAsync(() -> {
            HttpSession session = sessionHandler.createSession(authLoginBO.getLoginName());
            // TODO - 本地会话创建成功之后，需要调用企业统一认证中心的接口，获取权限列表，然后和本地会话绑定，存放到redis中
            sessionHandler.setAttribute(SessionCacheKeyEnum.CURRENT_USER_INFO, authLoginBO);
            return session.getId();
        }).exceptionally(throwable -> {
            throw new ApplicationException(throwable.getCause());
        });
    }

    /**
     * 移除本地会话
     */
    BenchmarksFuture<Boolean> removeLocalSession() {
        // TODO - 本地会话注销成功之后，需要调用企业统一认证中心的接口，注销全局会话！
        return BenchmarksFuture.supplyAsync(
                sessionHandler::removeSession
        ).exceptionally(throwable -> {
            throw new ApplicationException(throwable.getCause());
        });
    }

    /**
     * 获取本地会话
     */
    BenchmarksFuture<AuthLoginBO> readLocalSession() {
        return BenchmarksFuture.supplyAsync(
                SessionUserUtils::getCurrentUser
        ).exceptionally(throwable -> {
            throw new ApplicationException(throwable.getCause());
        });
    }

}
